﻿namespace Agile.Infra.Repository;

/// <summary>脏属性集合</summary>
/// <remarks>
/// 脏数据需要并行高性能，要节省内存，允许重复。
/// 普通集合加锁成本太高，并发集合内存消耗太大，并发字典只有一两项的时候也要占用7.9k内存。
/// </remarks>
[Serializable]
public class ChangedPropertyCollection : IEnumerable<String>
{
    private String[] _items = new String[8];
    private Int32 _size;
    private Int32 _length;

    /// <summary>获取或设置与指定的属性是否有脏数据。</summary>
    /// <param name="item"></param>
    /// <returns></returns>
    public Boolean this[String item]
    {
        get => Contains(item);
        set
        {
            if (value)
                Add(item);
            else
                Remove(item);
        }
    }

    private void Add(String item)
    {
        if (Contains(item)) return;

        // 抢位置
        var n = Interlocked.Increment(ref _length);

        var ms = _items;
        while (ms.Length < _length)
        {
            // 扩容
            var arr = new String[ms.Length * 2];
            Array.Copy(ms, arr, ms.Length);
            if (Interlocked.CompareExchange(ref _items, arr, ms) == ms) break;

            ms = _items;
        }

        _items[n - 1] = item;

        Interlocked.Increment(ref _size);
    }

    private void Remove(String item)
    {
        var len = _length;
        var ms = _items;
        if (len > ms.Length) len = ms.Length;
        for (var i = 0; i < len; i++)
        {
            if (ms[i] == item)
            {
                ms[i] = null;

                Interlocked.Decrement(ref _size);
            }
        }
    }

    private Boolean Contains(String item)
    {
        var len = _length;
        var ms = _items;
        if (len > ms.Length) len = ms.Length;
        for (var i = 0; i < len; i++)
        {
            if (ms[i] == item) return true;
        }

        return false;
    }

    /// <summary>清空</summary>
    public void Clear()
    {
        _length = 0;
        _size = 0;
        Array.Clear(_items, 0, _items.Length);
    }

    /// <summary>个数</summary>
    public Int32 Count => _size;

    /// <summary>枚举迭代</summary>
    /// <returns></returns>
    public IEnumerator<String> GetEnumerator()
    {
        var len = _length;
        var ms = _items;
        if (len > ms.Length) len = ms.Length;
        for (var i = 0; i < len; i++)
        {
            if (ms[i] != null) yield return ms[i];
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

    public String[] ToArray()
    {
        var len = _length;
        var ms = _items;
        if (len > ms.Length) len = ms.Length;
        var list = new List<String>();
        for (var i = 0; i < len; i++)
        {
            if (ms[i] != null) list.Add(ms[i]);
        }
        return list.ToArray();
    }
}
